// Copyright (c) 2022 Presto Labs Pte. Ltd.
// Author: daniel

#pragma once

#include <memory>
#include <optional>
#include <string>
#include <vector>

#include <fmt/format.h>

#include "coin2/exchange/base/symbology/spot_product.h"
#include "coin2/exchange/prex/symbology/currency.h"

namespace coin2::exchange::prex::symbology {

class PrexProduct : public base::symbology::SpotProduct<PrexCurrency, PrexProduct>,
                    public base::symbology::ProductFromStrMixin<PrexProduct> {
 public:
  using BaseProduct = base::symbology::SpotProduct<PrexCurrency, PrexProduct>;

 public:
  PrexProduct(const PrexCurrency& base, const PrexCurrency& quote)
      : SpotProduct(
            base,
            quote,
            ToPrexNativeProduct(base, quote),
            ToPrexNativeProduct(base, quote),
            base::market::ExchangeType::Prex) {}

 public:
  static std::unique_ptr<PrexProduct> FromNativeStrImpl(
      const std::string& native_product,
      int64_t resolve_timestamp) {
    std::unique_ptr<PrexCurrency> base_ptr;
    std::unique_ptr<PrexCurrency> quote_ptr;
    bool success = ParsePrexNativeProduct(native_product, resolve_timestamp, &base_ptr, &quote_ptr);
    if (success) {
      return std::make_unique<PrexProduct>(*base_ptr, *quote_ptr);
    } else {
      return {};
    }
  }

 private:
  static bool ParsePrexNativeProduct(
      const std::string& product_str,
      int64_t resolve_timestamp,
      std::unique_ptr<PrexCurrency>* base_ptr,
      std::unique_ptr<PrexCurrency>* quote_ptr) {
    using base::market::MarketType;

    std::string quote;
    if (StringEndsWith(product_str, "USDT")) {
      quote = std::string("USDT");
    } else if (StringEndsWith(product_str, "USD")) {
      quote = std::string("USD");
    } else {
      CHECK(false);
    }

    if (product_str.size() <= quote.size() ||
        product_str.substr(product_str.size() - quote.size()) != quote) {
      return false;
    }
    const std::string base = product_str.substr(0, product_str.size() - quote.size());

    *base_ptr = PrexCurrency::FromNativeStrNoThrow(base, resolve_timestamp);
    *quote_ptr = PrexCurrency::FromNativeStrNoThrow(quote, resolve_timestamp);
    if (*base_ptr && *quote_ptr) {
      return true;
    } else {
      return false;
    }
  }

  static std::string ToPrexNativeProduct(const PrexCurrency& base, const PrexCurrency& quote) {
    using base::market::MarketType;
    return fmt::format(
        "{}{}",
        base.native_currency(MarketType::Spot),
        quote.native_currency(MarketType::Spot));
  }
};

}  // namespace coin2::exchange::prex::symbology
